/* -*-C-*-
*******************************************************************************
*
* File:         Preferences.m
* RCS:          $Header: /usr/local/lib/cvs/EnhanceMail/Preferences.m,v 1.1.1.23 1996/04/09 16:18:58 cedman Exp $
* Description:  
* Author:       Carl Edman
* Created:      Wed Nov  1 09:58:01 1995
* Modified:     Tue Apr  9 11:46:04 1996 (Carl Edman) cedman@capitalist.princeton.edu
* Language:     C
* Package:      N/A
* Status:       Experimental (Do Not Distribute)
*
* (C) Copyright 1995, but otherwise this file is perfect freeware.
*
*******************************************************************************
*/

#define PREFEXTERN
#import "Preferences.h"
#undef PREFEXTERN
#import "EnhanceMail.h"
#import "XFace.h"
#import "XImageURL.h"
#import "regexp.h"
#import <ctype.h>
#import <math.h>
#import <netdb.h>

#define MSG_OK NXLoadLocalizedStringFromTableInBundle("Buttons", nil, "OK", NULL)
#define MSG_BAD_XFACE NXLocalizedStringFromTableInBundle("Localizable", EnhanceBundle, "%s does not contain monochrome 48x48 image.", NULL, Error message for bad XFace)
#define MSG_BAD_REGEXP NXLocalizedStringFromTableInBundle("Localizable", EnhanceBundle, "%s is not a valid regular expression.", NULL, Error message for bad Regexp)

#define DEFAULT_QUOTE_INTRO NXLocalizedStringFromTableInBundle("Localizable", EnhanceBundle, "You wrote:", NULL, Default introduction to quotes in replies)

static struct PrefDefault
   {
   struct PrefDefault *next;
   enum { bool=1, string, integer, color } type;
   union
      {
      BOOL *bool;
      const char **string;
      int *integer;
      NXColor *color;
      } location;
   id control;
   char name[0];
   } *prefroot=0;

static HashTable *PanelHash=nil;
static NXImage *delete_image=nil;
static id XImageURLButton=nil;
static id XFaceButton=nil;

@implementation EnhancePreferences
+ finishLoading:(struct mach_header *)header
   {
   id pref;
   char path[MAXPATHLEN+1];
   
   if (EnhanceBundle==0) EnhanceBundle=[NXBundle bundleForClass:[self class]];
   [self poseAs:[self superclass]];

   if ([EnhanceBundle getPath:path forResource:"delete" ofType:"tiff"])
      delete_image=[[NXImage alloc] initFromFile:path];
   
   pref=[Preferences new];

   if ([EnhanceBundle getPath:path forResource:"EnhancePreferences" ofType:"nib"])
      [NXApp loadNibFile:path owner:pref];

   [pref addPanel:NXGetNamedObject("Quoting_Box2",pref)];
   [pref addPanel:NXGetNamedObject("Images_Box2",pref)];
   [pref addPanel:NXGetNamedObject("Options_Box2",pref)];
   
   EnhanceQuoteReplies=NO;
   EnhanceQuoteColoring=NO;
   EnhanceLaunchMailboxes=NO;
   EnhanceLaunchAddresses=NO;
   EnhanceInsertSignature=NO;
   EnhanceRepliesWithOriginals=NO;
   EnhanceRestoreDeletesDraft=NO;
   EnhanceInsertXFace=NO;
   EnhanceInsertXImageURL=NO;
   EnhanceShowXImageURL=NO;
   EnhanceShowXFace=NO;
   EnhanceBackSpaceBackPages=NO;
   EnhanceAutoCompact=NO;
   EnhanceAutoSpellCheck=NO;
   EnhanceQuotePrefix="> %0";
   EnhanceQuoteRegex="^[>:# ]*[>:#] ?";
   EnhanceSignatureSeparator="---\n";
   EnhanceSignatureFilename=".signature";
   EnhanceQuoteIntro=DEFAULT_QUOTE_INTRO;
   EnhanceQuoteColor=NX_COLORBLACK;
   EnhanceXFace="";
   EnhanceXImageURL="";
   EnhanceFlagReplies=NO;
   EnhanceHTTPProxy="";

   [pref registerBool:"QuoteReplies"
             location:&EnhanceQuoteReplies
              control:"Quoting_Box2_Quoting_Quote Replies"];
   [pref registerBool:"QuoteColoring"
             location:&EnhanceQuoteColoring
              control:"Quoting_Box2_Quoting_Color Quotes"];
   [pref registerBool:"LaunchMailboxes"
             location:&EnhanceLaunchMailboxes
              control:"Options_Box2_Other_Form_Open Mailboxes Window on Launch"];
   [pref registerBool:"LaunchAddresses"
             location:&EnhanceLaunchAddresses
              control:"Options_Box2_Other_Form_Open Addresses Window on Launch"];
   [pref registerBool:"InsertSignature"
             location:&EnhanceInsertSignature
              control:"Quoting_Box2_Signatures_Insert Signatures"];
   [pref registerBool:"RepliesWithOriginals"
             location:&EnhanceRepliesWithOriginals
              control:"Options_Box2_Other_Form_Keep Replies with Originals"];
   [pref registerBool:"RestoreDeletesDraft"
             location:&EnhanceRestoreDeletesDraft
              control:0];
   [pref registerBool:"InsertXFace"
             location:&EnhanceInsertXFace
              control:"Images_Box2_XFace_Form_Send XFace"];
   [pref registerBool:"ShowXFace"
             location:&EnhanceShowXFace
              control:"Images_Box2_XFace_Form_Show XFace"];
   [pref registerBool:"InsertXImageURL"
             location:&EnhanceInsertXImageURL
              control:"Images_Box2_XImageURL_Form_Send XImageURL"];
   [pref registerBool:"ShowXImageURL"
             location:&EnhanceShowXImageURL
              control:"Images_Box2_XImageURL_Form_Show XImageURL"];
   [pref registerBool:"BackSpaceBackPages"
             location:&EnhanceBackSpaceBackPages
              control:0];
   [pref registerBool:"AutoCompact"
             location:&EnhanceAutoCompact
              control:"Options_Box2_Other_Form_Automatically compact mailboxes"];
   [pref registerBool:"AutoSpellCheck"
             location:&EnhanceAutoSpellCheck
              control:"Options_Box2_Other_Form_Automatically spell check messages"];
   [pref registerString:"QuoteIntro"
               location:&EnhanceQuoteIntro
                control:"Quoting_Box2_Quoting_Form_Introduction"];
   [pref registerString:"QuotePrefix"
               location:&EnhanceQuotePrefix
                control:"Quoting_Box2_Quoting_Form_Prefix"];
   [pref registerString:"QuoteRegex"
               location:&EnhanceQuoteRegex
                control:"Quoting_Box2_Quoting_Form_Regex"];
   [pref registerString:"XFace"
               location:&EnhanceXFace
                control:"Images_Box2_XFace_Path"];
   [pref registerString:"XImageURL"
               location:&EnhanceXImageURL
                control:"Images_Box2_XImageURL_Path"];
   [pref registerString:"SignatureFilename"
               location:&EnhanceSignatureFilename
                control:"Quoting_Box2_Signatures_Form_Filename"];
   [pref registerString:"SignatureSeparator"
               location:&EnhanceSignatureSeparator
                control:"Quoting_Box2_Signatures_Form_Separator"];
   [pref registerString:"HTTPProxy"
               location:&EnhanceHTTPProxy
                control:"Images_Box2_XImageURL_Proxy"];
   [pref registerColor:"QuoteColor"
              location:&EnhanceQuoteColor
               control:"Quoting_Box2_Quoting_Color"];
   [pref registerBool:"FlagReplies"
             location:&EnhanceFlagReplies
              control:"Options_Box2_Other_Form_Flag Replies"];

   XFaceButton=NXGetNamedObject("Images_Box2_XFace_Box_Image",pref);
   XImageURLButton=NXGetNamedObject("Images_Box2_XImageURL_Box_Image",pref);
   
   [pref upLoad];
   return self;
   }

- (void)downLoad
   {
   struct PrefDefault *pd;
   const char *appname, *tmp;
   char buf[1024];

   appname=[NXApp appName];

   for(pd=prefroot;pd;pd=pd->next) if (pd->control!=nil) switch (pd->type)
      {
    case bool:
      *pd->location.bool=[pd->control state];
      break;
    case string:
      *pd->location.string=[pd->control stringValue];
      break;
    case integer:
      *pd->location.integer=[pd->control intValue];
      break;
    case color:
      *pd->location.color=[pd->control color];
      break;
      }

   for(pd=prefroot;pd;pd=pd->next) switch (pd->type)
      {
    case bool:
      NXWriteDefault(appname,pd->name,*(pd->location.bool) ? "YES" : "NO");
      break;
    case string:
      NXWriteDefault(appname,pd->name,*(pd->location.string));
      break;
    case integer:
      sprintf(buf,"%d",*(pd->location.integer));
      NXWriteDefault(appname,pd->name,buf);
      break;
    case color:
      if (tmp=NXColorName(*(pd->location.color)))
         NXWriteDefault(appname,pd->name,tmp);
      else
         {
         float red,green,blue;
         
         NXConvertColorToRGB(*(pd->location.color),&red,&green,&blue);
         sprintf(buf,"RGB%02x%02x%02x",(int)rint(red*0xff),(int)rint(green*0xff),(int)rint(blue*0xff));
         NXWriteDefault(appname,pd->name,buf);
         }
      break;
      }
   
   [super downLoad];
   }

- (void)upLoad
   {
   int i;
   struct PrefDefault *pd;
   const char *appname, *value, *c;
   id colorlistlist=[NXColorList availableColorLists];

   [super upLoad];
   NXUpdateDefaults();
   appname=[NXApp appName];

   for(pd=prefroot;pd;pd=pd->next) if (value=NXReadDefault(appname,pd->name)) switch (pd->type)
      {
    case bool:
      if (strcasecmp(value,"YES")==0)
         *(pd->location.bool)=YES;
      else if (strcasecmp(value,"NO")==0)
         *(pd->location.bool)=NO;
      else
         [self logWarning:"Boolean default %s given bad value \"%s\".",appname,pd->name,value];
      break;
    case string:
      *(pd->location.string)=value;
      break;
    case integer:
      for(c=value;isspace(*c);c++);
      for(i=0;isdigit(*c);c++) i=i*10+(*c-'0');
      while(isspace(*c)) c++;
      if (*c=='\0')
         *(pd->location.integer)=i;
      else
         [self logWarning:"Integer default %s given bad value \"%s\".",appname,pd->name,value];
      break;
    case color:
      i=-1;
      if ((strncmp(value,"RGB",3)==0)&&(strlen(value)==9))
         {
         for(c=value+3,i=0;*c;c++)
            {
            if (*c>='0' && *c<='9')      i=16*i+*c-'0';
            else if (*c>='a' && *c<='f') i=16*i+*c-('a'-10);
            else if (*c>='A' && *c<='F') i=16*i+*c-('A'-10);
            else break;
            }
         
         if (*c)
            i=-1;
         else
            {
            *(pd->location.color)=NXConvertRGBToColor(((i>>16)&0xff)/255.0,((i>>8)&0xff)/255.0,(i&0xff)/255.0);
            i=0;
            }
         }
      
      if (i<0) for(i=[colorlistlist count]-1;i>=0;i--)
         {
         if (NXFindColorNamed([[colorlistlist objectAt:i] name],value,pd->location.color))
            break;
         }
      
      if (i<0)
         [self logWarning:"Color default %s given bad value \"%s\".",appname,pd->name,value];
      break;
      }

   for(pd=prefroot;pd;pd=pd->next) if (pd->control!=nil) switch (pd->type)
      {
    case bool:
      [pd->control setState:*(pd->location.bool)];
      break;
    case string:
      [pd->control setStringValue:*(pd->location.string)];
      break;
    case integer:
      [pd->control setIntValue:*(pd->location.integer)];
      break;
    case color:
      [pd->control setColor:*(pd->location.color)];
      break;
      }
   
   if (XFaceButton!=nil)
      {
      NXImage *image;
      NXRect r;

      [XFaceButton getBounds:&r];
      image=[[NXImage alloc] initFromFile:EnhanceXFace xFaceSize:&r.size];
      if (image==nil) image=delete_image;
      [XFaceButton setImage:image];
      }

   if (XImageURLButton!=nil)
      {
      NXImage *image;
      NXRect r;

      [XImageURLButton getBounds:&r];
      image=[[NXImage alloc] initURL:EnhanceXImageURL];
      if (image==nil) image=delete_image;
      [image setScalable:YES];
      [image setSize:&r.size];
      [XImageURLButton setImage:image];
      }
   }

- controlFor:(const void *)location
   {
   struct PrefDefault *pd;
   for(pd=prefroot;pd;pd=pd->next)
      if ((pd->location.string)==location) return pd->control;
   
   return nil;
   }

- addPanel:panel
   {
   id cell;
   id popuplist;
   int tag;
   
   if (panel==nil) return nil;
   
   popuplist=[[viewButton cell] target];
   for(tag=0;[popuplist findCellWithTag:tag]!=nil;tag++);
   
   cell=[popuplist addItem:[[panel window] title]];
   [cell setTarget:self];
   [cell setAction:@selector(changeView:)];
   [cell setTag:tag];

   if (PanelHash==nil) PanelHash=[[HashTable alloc] initKeyDesc:"i" valueDesc:"@"];
   [PanelHash insertKey:(const void *)tag value:panel];
   return self;
   }

- registerBool:(const char *)name location:(BOOL *)loc control:(const char *)con
   {
   struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=bool;
   pd->location.bool=loc;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   return self;
   }

- registerString:(const char *)name location:(const char **)loc control:(const char *)con;
   {
   struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=string;
   pd->location.string=loc;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   return self;
   }

- registerInteger:(const char *)name location:(int *)loc control:(const char *)con
   {
   struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=integer;
   pd->location.integer=loc;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   return self;
   }

- registerColor:(const char *)name location:(NXColor *)loc control:(const char *)con
   {
   struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=color;
   pd->location.color=loc;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   return self;
   }

- changeView:sender
   {
   id ret,item,panel,ogeneralview;
   int tag;

   if (((item=[viewButton selectedPopUpItem])==nil)
       ||((tag=[item tag])==0)
       ||(PanelHash==nil)
       ||((panel=[PanelHash valueForKey:(const void *)tag])==nil))
      return [super changeView:sender];

   ogeneralview=generalView;
   generalView=panel;
   [item setTag:0];
   ret=[super changeView:sender];
   [item setTag:tag];
   generalView=ogeneralview;

   return ret;
   }

- noteChangeXFace:sender
   {
   id XFace=[self controlFor:&EnhanceXFace];
   
   if (XFace && XFaceButton)
      {
      NXRect r;
      NXImage *image;
      const char *val;
      
      val=[XFace stringValue];
      [XFaceButton getBounds:&r];
      image=[[NXImage alloc] initFromFile:val xFaceSize:&r.size];
      if (image==nil) image=delete_image;
      [XFaceButton setImage:image];
      }
   
   return [super noteChange:sender];
   }

- noteChangeXImageURL:sender
   {
   id XImageURL=[self controlFor:&EnhanceXImageURL];
   
   if (XImageURL && XImageURLButton)
      {
      NXRect r;
      NXImage *image;
      const char *val;
      
      val=[XImageURL stringValue];
      [XImageURLButton getBounds:&r];
      image=[[NXImage alloc] initURL:val];
      if (image==nil) image=delete_image;
      [image setScalable:YES];
      [image setSize:&r.size];
      [XImageURLButton setImage:image];
      }
   
   return [self noteChange:sender];
   }

- setXFace:sender
   {
   id open;
   const char *const types[] = { "tiff", 0 };
   id XFaceC=[self controlFor:&EnhanceXFace];
   
   if (((XFaceC=[self controlFor:&EnhanceXFace])==nil)
       || ((open=[OpenPanel new])==nil)
       || ([open allowMultipleFiles:NO]==nil)
       || ([open runModalForDirectory:[XFaceC stringValue] file:0 types:types]!=NX_OKTAG)
       || ([open filename]==0))
      return nil;

   [XFaceC setStringValue:[open filename]];
   return [self noteChangeXFace:sender];
   }
@end
